/*
 * ﻿Copyright (C) 2011-2014 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.aspectjutil.adviseutil;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.weaver.loadtime.WeavingURLClassLoader;

import com.sun.tools.attach.VirtualMachine;

/**
 * Loader for AspectJ weaver javaagent and dynamically attach it to running process. <br /> 
 * Note: only supports Sun's JVM (currently). <br />
 * Though this class is not necessary in general because of existence of {@link WeavingURLClassLoader}  
 * class in AspectJ, in some special case, this with DynamicAspectJWeaverAgent class becomes 
 * useful for applying AspectJ's <code>Agent</code> dynamically. <br /> 
 * It is highly recommended to enabling LTW by rather giving javaagent option at application launch 
 * than using this, in order to eliminate possibility of encountering class loader entanglement. <br />
 * <h4>Usage example</h4>
 * <ul>
 * <li>For the case that the target machine has AspectJ SDK installed and the ASPECTJ_HOME system 
 * environment variable has been set:
 * <ul>
 * <li>And if you can load DynamicAspectJWeaverAgent class from application's classpath.  
 * <blockquote>
 * <code>
 * AspectJWeaverAgentLoader aspectJWeaverAgentLoader = new AspectJWeaverAgentLoader();<br />
 * aspectJWeaverAgentLoader.enableWeaverAgent( DynamicAspectJWeaverAgent.class);
 * </code>
 * </blockquote>
 * </li>
 * <li>If you cannot
 * <blockquote>
 * <code>
 * String pathToDynamicAspectJWeaverAgentJarFile<br /> 
 * = "somehow/get/path/to/aspectjutil-dynamicagent.jar";<br />
 * AspectJWeaverAgentLoader aspectJWeaverAgentLoader = new AspectJWeaverAgentLoader();<br />
 * aspectJWeaverAgentLoader.enableWeaverAgent( pathToDynamicAspectJWeaverAgentJarFile);
 * </code>
 * </blockquote>
 * </li>
 * </ul>
 * </li>
 * <li>For the case that the target machine does not have the ASPECTJ_HOME system environment set, 
 * then, in the above examples, set path to aspectjweaver.jar file with {@link #setAspectjWeaverJarFile} 
 * method before executing either one of overloaded <code>enableWeaverAgent</code> method.
 * </li>
 * </ul>
 * For more custom controls to add additional .jar file(s) to application's system classpath or 
 * to provide arguments to AspectJ weaver java-agent if you need, see the next usage guide.
 * 
 * <h4>Usage guide:</h4>
 * <ol>
 * <li>Set .jar file containing dynamic agent class what is actual caller of AspectJ weaver javaagent by 
 * {@link #setDynamicWeaverAgentJarFile(String)} method. <br />
 * (The .jar file for <code>DynamicAspectJWeaverAgent</code> class can be given for this: its Maven 
 * ID is <code>com.newmainsoftech:aspectjutil-dynamicagent</code>.)<br />
 * If such .jar file has been already loaded, then, instead of using <code>setDynamicWeaverAgentJarFile</code> 
 * method, the loaded dynamic agent class can be specified directly by using either 
 * {@link #enableWeaverAgent(String)} method or {@link #enableWeaverAgent(Class)} method at last step below. 
 * </li>
 * <li>Set path to aspectjweaver.jar file (containing agent class in what static agent method is defined 
 * but dynamic one) by {@link #setAspectjWeaverJarFile(String)}. <br />
 * This step can be skipped as long as using <code>DynamicAspectJWeaverAgent</code> class as described 
 * at the previous step and it will be OK for <code>DynamicAspectJWeaverAgent</code> to automatically load 
 * aspectjweaver.jar file from the AspectJ's installation point specified by <code>ASPECTJ_HOME</code> 
 * system environment variable. 
 * </li>
 * <li>Set custom arguments (being given to the dynamic agent method in the .jar file specified at the 
 * first step above) by {@link #setAspectjWeaverAgentMethodArg(String)} method. <br />
 * Regarding the arguments being actually given to the dynamic agent method, the actaul argument will be 
 * consisted of the followings (the order of the descriptions 
 * here isn't necessary to be same order giving to the dynamic agent method): <br />
 * <ul>
 * <li>The custom arguments set by {@link #setAspectjWeaverAgentMethodArg(String)} method. 
 * The argument name for this can be obtained by {@link #getAspectjWeaverAgentMethodArgName()} method.</li>
 * <li>The path to aspectjweaver.jar file. 
 * The argument name for this can be obtained by {@link #getAspectjWeaverJarArgName()} method.</li>
 * <li>The path of additional .jar files. 
 * The argument name for this can be obtained by {@link #getAdditionalJarPathsArgName()} method.</li>
 * </ul> 
 * </li>
 * <li>Set path to additional .jar files being added to application's system class path by 
 * {@link #addToAdditionalJarPaths(String)}.<br />
 * By default, each additional .jar file will be checked with system class loader whether it has 
 * already been loaded every time when <code>addToAdditionalJarPaths</code> method is used. To disable 
 * checking, give <code>false</code> to {@link #setPreloadCheckOfAdditionalJarEnabled(boolean)} method.
 * </li>
 * <li>Call {@link #enableWeaverAgent()} method.<br />
 * {@link #enableWeaverAgent(String)} method or {@link #enableWeaverAgent(Class)} method can be used as 
 * an alternate. <br />
 * Those methods will do nothing when AspectJ's LTW has been already enabled.
 * </li>
 * </ol>
 * 
 * @author <a href="mailto:artymt@gmail.com">Arata Y.</a>
 */
public class AspectJWeaverAgentLoader extends AgentLoader {
/* In this class, intentionally avoided to make reference to AspectJ's Agent class since  
 * implementing DynamicAspectJWeaverAgent class to have the feature of dynamically loading 
 * aspectweaver.jar file when it's not in class-path by using given Instrumentation object.  
 * In short, aspectweaver.jar file may not have been loaded and it's supported scenario. 
 */
		
	public static final String AspectjWeaverAgentClassName = "org.aspectj.weaver.loadtime.Agent";
		public static String getAspectjWeaverAgentClassName() {
			return AspectjWeaverAgentClassName;
		}

	// For sanity check of LTW --------------------------------------------------------------------
	/**
	 * For sanity check whether LTW by aspectjweaver JavaAgent has been already enabled.
	 * @return true when LTW by aspectjweaver JavaAgent has been already enabled.
	 */
	boolean isWeaverAgentEnabled() {
		return false;
	}
		/**
		 * Aspect for {@link AspectJWeaverAgentLoader#isWeaverAgentEnabled()} method to reflect 
		 * whether LTW by aspectjweaver JavaAgent has been already enabled.
		 * 
		 * @author <a href="mailto:artymt@gmail.com">Arata Y.</a>
		 */
		@Aspect
		public static abstract class WeaverAgentCheckAbstractAspect {
			Logger logger = Logger.getLogger( this.getClass().getName());
				Logger getLogger() {
					return logger;
				}
			
			@Pointcut
			public abstract void pointcutAtExecutionOfIsWeaverAgentEnabled();
				// Made this "abstract" so no chance of cross-cutting by CTW by mistake. 
				// Concrete aspect is defined in aop.xml. 
			@Around( value="pointcutAtExecutionOfIsWeaverAgentEnabled()")
			public Object aroundAdvisedExecutionOfIsWeaverAgentEnabled( 
					ProceedingJoinPoint proceedingJoinPoint) 
			{
				// Just logging some info --------------------------------------------------------- 
				String aspectjweaverJarPath = "";
					try {
						Class<?> agentClass 
						= Class.forName( AspectJWeaverAgentLoader.getAspectjWeaverAgentClassName());
						URL aspectjweaverJarUrl 
						= agentClass.getProtectionDomain().getCodeSource().getLocation();
						File aspectjweaverJarFile = new File( aspectjweaverJarUrl.toURI());
						aspectjweaverJarPath 
						= String.format( 
								" (loaded from %1$s)",
								aspectjweaverJarFile.getAbsolutePath()
								);
					}
					catch( Exception exception) { // Do nothing
					}
				
				Logger logger = getLogger();
				if ( logger.isLoggable( Level.INFO)) {
					logger.log(
							Level.INFO,
							String.format(
									"aspectjweaver's Agent%1$s has been already enabled as JavaAgent.",
									aspectjweaverJarPath
									)
							);
				}
				// --------------------------------------------------------------------------------
				
				return true;
			}
		}
	// --------------------------------------------------------------------------------------------
	/**
	 * Check whether attribute value for specified manifest attribute key by <code>attributeKey</code> 
	 * input in manifest of .jar file given by <code>jarFile</code> input.
	 * @param jarFile
	 * @param attributeKey
	 * @return true when value for specified manifest attribute key exists in manifest of provided 
	 * .jar file, otherwise false. 
	 * @throws IOException
	 */
	protected boolean hasManifestAttribute( final JarFile jarFile, final String attributeKey) 
	throws IOException {
		Manifest manifest = jarFile.getManifest();
		Attributes mainAttributes = manifest.getMainAttributes();
		String attributeValue = mainAttributes.getValue( attributeKey);
		if ( attributeValue != null) return true;
		
		for( Entry<String, Attributes> entry : manifest.getEntries().entrySet()) {
			attributeValue = entry.getValue().getValue( attributeKey);
			if ( attributeValue != null) return true;
		} // for
		
		return false;
	}
	
	// Regarding aspectjweaver.jar file -----------------------------------------------------------
	public static final String AspectjWeaverJarArgName = "aspectjweaverjar";
		/**
		 * Return name of argument for aspectjweaver.jar file specified by 
		 * {@link #setAspectjWeaverJarFile(String)} method. The argument will be given to 
		 * dynamic agent specified by {@link #setDynamicWeaverAgentJarFile(String)} method,  
		 * {@link #enableWeaverAgent(Class)} method or {@link #enableWeaverAgent(String)} method 
		 * in order to call agent method in the aspectjweaver.jar file.
		 * @return {@value #AspectjWeaverJarArgName}
		 */
		public static String getAspectjWeaverJarArgName() {
			return AspectJWeaverAgentLoader.AspectjWeaverJarArgName;
		}
	private File aspectjWeaverJarFile;
		public synchronized File getAspectjWeaverJarFile() {
			return aspectjWeaverJarFile;
		}
		public synchronized String getAspectjWeaverJarPath() {
			File aspectjWeaverJarFileObj = getAspectjWeaverJarFile();
			if ( aspectjWeaverJarFileObj == null) return null;
			return aspectjWeaverJarFileObj.getAbsolutePath();
		}
		
		/**
		 * Set path to aspectjweaver.jar file (or extension of it). 
		 * The given path will be handed over as parameter to dynamic weaver agent (specified by 
		 * {@link #setDynamicWeaverAgentJarFile(String)} method) via 
		 * {@link VirtualMachine#loadAgent(String, String)} method. <br />
		 * If aspectjweaver.jar file doesn't need to be loaded (since it's already loaded) but 
		 * just calling premain method of AspectJ's Agent, then do not need to set it before 
		 * calling either of {@link #enableWeaverAgent()}, {@link #enableWeaverAgent(String)}, 
		 * and {@link #enableWeaverAgent(Class)} methods. <br />
		 * With expectation for aspectjweaver.jar to be provided by <code>aspectjWeaverJarPath</code> 
		 * input, the .jar file pointed by <code>aspectjWeaverJarPath</code> input is expected to 
		 * contain MANIFEST.MF having Premain-Class attribute declared. 
		 * @param aspectjWeaverJarPath path to aspectjweaver.jar file (or extension of it).
		 */
		public synchronized void setAspectjWeaverJarFile( final String aspectjWeaverJarPath) {
			File file = new File( aspectjWeaverJarPath);
			String agentClass = null;
			boolean isAgentFound = false;
			Exception cause = null;
			try {
				JarFile jarFile = new JarFile( file.getAbsoluteFile());
				isAgentFound = hasManifestAttribute( jarFile, "Premain-Class");
			}
			catch( Exception exception) {
				cause = exception;
			}
			finally {
				if ( !isAgentFound) {
					IllegalArgumentException exception 
					= new IllegalArgumentException(
							String.format(
									"\"%1$s\" does not point to accessible valid .jar file containing " 
									+ "Java Agent.",
									aspectjWeaverJarPath
									), 
							cause
							);
					throw exception;
				}
			}
			
			aspectjWeaverJarFile = file;
		}
	// --------------------------------------------------------------------------------------------
	// Regarding options being passed to premain method of Agent class in aspectjweaver.jar file --
	public static final String AspectjWeaverAgentMethodArgName = "agent_method_arg";
		/**
		 * @return name of argument being given to agentmain method of agent class in .jar file 
		 * specified by {@link #setDynamicWeaverAgentJarFile(String)} method, 
		 * {@link #enableWeaverAgent(String)} method or {@link #enableWeaverAgent(Class)} method.
		 */
		public static String getAspectjWeaverAgentMethodArgName() {
			return AspectjWeaverAgentMethodArgName;
		}
	private String aspectjWeaverAgentMethodArg;
		public synchronized String getAspectjWeaverAgentMethodArg() {
			return aspectjWeaverAgentMethodArg;
		}
		/**
		 * Set value of argument (of what name can be obtained by {@link #getAspectjWeaverAgentMethodArgName} 
		 * method) being given to agentmain method of agent class in .jar file specified by 
		 * {@link #setDynamicWeaverAgentJarFile(String)} method, 
		 * {@link #enableWeaverAgent(String)} method or {@link #enableWeaverAgent(Class)} method. 
		 *  
		 * @param aspectjWeaverAgentMethodArg
		 */
		public synchronized void setAspectjWeaverAgentMethodArg( final String aspectjWeaverAgentMethodArg) {
			String aspectjWeaverAgentMethodArgCopy 
			= ((aspectjWeaverAgentMethodArg == null) ? "" : aspectjWeaverAgentMethodArg.trim());
				if ( aspectjWeaverAgentMethodArg.length() < 1) aspectjWeaverAgentMethodArgCopy = null;
			this.aspectjWeaverAgentMethodArg = aspectjWeaverAgentMethodArgCopy;
		}
	// --------------------------------------------------------------------------------------------
		

	// Regarding parameter of additional jars to be added to application class path ---------------
	private Set<File> additionalJarPaths = new CopyOnWriteArraySet<File>();
		/**
		 * Obtain string of paths added by {@link #addToAdditionalJarPaths(String)} method. 
		 * Each path is delimited by character given by <code>File.pathSeparator</code> method.  
		 * @return string of paths added by {@link #addToAdditionalJarPaths(String)} method or 
		 * empty string.
		 */
		public String getAdditionalJarPaths() {
			String additionalJarPathsStr = "";
				Iterator<File> iterator = additionalJarPaths.iterator();
				while( iterator.hasNext()) {
					File file = iterator.next();
					additionalJarPathsStr 
					= additionalJarPathsStr + file.getAbsolutePath() + File.pathSeparator;
				} // whlie
			return additionalJarPathsStr;
		}
		boolean isPreloadCheckOfAdditionalJarEnabled = true;
			public synchronized boolean isPreloadCheckOfAdditionalJarEnabled() {
				return isPreloadCheckOfAdditionalJarEnabled;
			}
			/**
			 * Set flag controlling whether check if .jar file has been already loaded within 
			 * {@link #addToAdditionalJarPaths(String)} method. 
			 * Default value is true.
			 * 
			 * @param isPreloadCheckOfAdditionalJarEnabled
			 */
			public synchronized void setPreloadCheckOfAdditionalJarEnabled(
					boolean isPreloadCheckOfAdditionalJarEnabled) {
				this.isPreloadCheckOfAdditionalJarEnabled = isPreloadCheckOfAdditionalJarEnabled;
			}

		protected Set<File> getClassPathFileList() {
			Set<File> classPathFileSet = new HashSet<File>();
			String systemClassPath = System.getProperty( "java.class.path");
			for( String archivePath : systemClassPath.split( File.pathSeparator)) {
				File archiveFile = new File( archivePath);
				if ( archiveFile.exists()) {
					classPathFileSet.add( archiveFile.getAbsoluteFile());
				}
			} // for
			return classPathFileSet;
		}

		public static final String AdditionalJarPathsArgName = "jar_adition";
			/**
			 * Return name of argument for additional jar paths what will be added via 
			 * {@link #addToAdditionalJarPaths(String)} method.  
			 * The argument will be given to dynamic agent method specified by 
			 * {@link #setDynamicWeaverAgentJarFile(String)} method, {@link #enableWeaverAgent(String)} 
			 * method or {@link #enableWeaverAgent(Class)} method. 
			 * 
			 * @return {@value #AdditionalJarPathsArgName}.
			 */
			public static String getAdditionalJarPathsArgName() {
				return AspectJWeaverAgentLoader.AdditionalJarPathsArgName;
			}
		
		/**
		 * Add path to .jar file given by <code>jarPath</code> input to the list of .jar files what 
		 * will be provided as an argument to agentmain method of the class in the .jar file 
		 * specified by {@link #setAspectjWeaverJarFile(String)} method or of the class given to 
		 * either {@link #enableWeaverAgent(Class)} or {@link #enableWeaverAgent(String)} methods.
		 * 
		 * @param jarPath
		 * @return true when given path is added. false when it has already been added before.
		 */
		public boolean addToAdditionalJarPaths( final String jarPath) {
			final File file = new File( jarPath);
			final String jarAbsolutePath = file.getAbsolutePath();
			
			JarFile jarFile;
				try {
					jarFile = new JarFile( jarAbsolutePath);
				}
				catch( Exception exception) {
					throw new IllegalArgumentException(
							String.format(
									"\"%1$s\" as given value of additionalJarPaths input does not point to " 
									+ "accessible .jar file.",
									jarPath
									),
							exception
							);
				}
			
			if ( isPreloadCheckOfAdditionalJarEnabled()) {
				for( String jarPathStr : getAdditionalJarPaths().split( File.pathSeparator)) {
					if ( jarPathStr == null) continue; // for
					if ( "".equals( jarPathStr.trim())) continue; // for
					
					File fileInPath = new File( jarPathStr);
						if ( !jarAbsolutePath.equals( fileInPath.getAbsolutePath())) continue;
						Logger logger = getLogger();
							if ( logger.isLoggable( Level.INFO)) {
								logger.info( 
										String.format(
												"Skipped adding %1$s to the %2$s parameter being handed over " 
												+ "to agent when agent is enabled by enableWeaverAgent method " 
												+ "since %1$s has been already added to %2$s parameter.", 
												jarPath, 
												getAdditionalJarPathsArgName()
												)
										);
							}
						return false;
				}
				
				String classToLoad = null;
				try {
					Manifest manifest = jarFile.getManifest();
					Attributes mainAttributes = manifest.getMainAttributes();
					if ( mainAttributes != null) {
						classToLoad = mainAttributes.getValue( Name.MAIN_CLASS);
					}
				}
				catch( Exception exception) { // Ignore
				}
				if ( classToLoad == null) {
					Enumeration<JarEntry> jarEntries = jarFile.entries();
					while( jarEntries.hasMoreElements()) {
						String jarEntryName = jarEntries.nextElement().getName();
						if ( jarEntryName.endsWith( ".class")) {
							if ( jarEntryName
									.substring( 0, jarEntryName.lastIndexOf( "/"))
									.contains( "$")) {
								continue; // while
							}
							classToLoad 
							= jarEntryName.replaceAll( "/", ".")
								.substring( 0, jarEntryName.length() - ".class".length());
							break; // while
						}
					} // while
				}
				if ( classToLoad == null) {
					Logger logger = getLogger();
					if ( logger.isLoggable( Level.INFO)) {
						logger.info( 
								String.format(
										"Since %1$s seems to contain only resources but classes, " 
										+ "going to simply add to the %2$s parameter (what will be handed " 
										+ "over to agent when agent is enabled by enableWeaverAgent method) " 
										+ "without checking whether it has been loaded before.",
										jarPath, getAdditionalJarPathsArgName()
										)
								);
					}
				}
				else {
					Class<?> loadedClass = null;
					try {
						loadedClass 
						= Class.forName( classToLoad, false, ClassLoader.getSystemClassLoader());
					}
					catch( Exception exception) { // ignore
					}
					if ( loadedClass != null) {
						String loadedArchivePath = null;
							try {
								File loadedArchiveFile 
								= new File( loadedClass.getProtectionDomain()
											.getCodeSource().getLocation().toURI());
								loadedArchivePath = loadedArchiveFile.getAbsolutePath();
							}
							catch( Exception exception) { // Just ignore exception
							} 
						
						Logger logger = getLogger();
						if ( logger.isLoggable( Level.INFO)) {
							logger.info( 
									String.format(
											"Skipped adding %1$s to the %2$s parameter being handed over " 
											+ "to agent when agent is enabled by enableWeaverAgent method " 
											+ "since %3$s class can be loaded from %4$s even before " 
											+ "adding the specified %1$s to application class path. " 
											+ "%nHere's dump of application system class path as refence " 
											+ "(although only system class path is not reliable source for " 
											+ "resolving class): %5$s",
											jarPath, 
											getAdditionalJarPathsArgName(),
											classToLoad,
											((loadedArchivePath == null) ? "archive" : loadedArchivePath),
											System.getProperty( "java.class.path")
											)
									);
						}
						return false;
					}
				}
			}
			
			return additionalJarPaths.add( file);
		}
		public boolean removeFromAdditionalJarPaths( String jarPath) {
			File file = new File( jarPath);
			return additionalJarPaths.remove( file);
		}
		public void clearAdditionalJarPaths() {
			additionalJarPaths.clear();
		}
		
	// --------------------------------------------------------------------------------------------
	
	private File dynamicWeaverAgentJarFile;
		public synchronized File getDynamicWeaverAgentJarFile() {
			return dynamicWeaverAgentJarFile;
		}
		public synchronized String getDynamicWeaverAgentJarPath() {
			File dynamicWeaverAgentJarFileObj = getDynamicWeaverAgentJarFile();
			if ( dynamicWeaverAgentJarFileObj == null) return null;
			return dynamicWeaverAgentJarFile.getAbsolutePath();
		}
		/**
		 * Set path to .jar file of agent class what has agentmain method being called.
		 * @param dynamicWeaverAgentJarPath
		 */
		public synchronized void setDynamicWeaverAgentJarFile( final String dynamicWeaverAgentJarPath) {
			// Validation on dynamicWeaverAgentJarPath --------------------------------------------
			File file = new File( dynamicWeaverAgentJarPath);
			boolean isAgentFound = false;
			Exception cause = null;
			try {
				JarFile jarFile = new JarFile( file.getAbsoluteFile());
				isAgentFound = hasManifestAttribute( jarFile, "Agent-Class");
			}
			catch( Exception exception) {
				cause = exception;
			}
			finally {
				if ( !isAgentFound) {
					IllegalArgumentException exception 
					= new IllegalArgumentException(
							String.format(
									"\"%1$s\" does not point to accessible valid .jar file containing " 
									+ "dynamically applicable Java Agent.",
									dynamicWeaverAgentJarPath
									)
							);
						if ( cause == null) {
							exception.initCause( cause);
						}
					throw exception;
				}
			}
			// ------------------------------------------------------------------------------------
			
			dynamicWeaverAgentJarFile = file;
		}
		
	protected static final String checkJarAdditionArgName = "checkjaraddition";
		protected static String getCheckJarAdditionArgName() {
			return checkJarAdditionArgName;
		}
		
	/**
	 * Construct arguments being given to agent what will be called by 
	 * {@link #enableJavaAgent(String, String)} method. 
	 * @return arguments for agent being called.
	 */
	protected synchronized String constructAgentParameters() {
		// Construct agentParameters -------------------------------------------------------------
		String agentParameters = "";
			String aspectjWeaverJarPath = getAspectjWeaverJarPath();
				if ( aspectjWeaverJarPath != null) {
					agentParameters 
					= AspectJWeaverAgentLoader.getAspectjWeaverJarArgName() 
						+ "=\"" + aspectjWeaverJarPath + "\"";
				}
			String aspectjWeaverAgentMethodArg = getAspectjWeaverAgentMethodArg();
				if ( aspectjWeaverAgentMethodArg != null) {
					agentParameters 
					= ((agentParameters == null) ? "" : agentParameters + ",")
						+ AspectJWeaverAgentLoader.getAspectjWeaverAgentMethodArgName() 
						+ "=" + aspectjWeaverAgentMethodArg;
				}
			String additionalJarPaths = getAdditionalJarPaths();
				if ( additionalJarPaths.length() > 0) {
					agentParameters 
					= ((agentParameters == null) ? "" : agentParameters + ",")
						+ AspectJWeaverAgentLoader.getAdditionalJarPathsArgName() 
						+ "=\"" + additionalJarPaths + "\"";
					agentParameters = agentParameters + "," 
							+ AspectJWeaverAgentLoader.getCheckJarAdditionArgName() + "=false";
						/* Ignore about DynamicAspectJWeaverAgent.Option.AspectjWeaverAgentMethodArg 
						 * since pre-check can be performed in this class according to
						 * isPreloadCheckOfAdditionalJarEnabled field value.
						 */
				}
			if ( agentParameters.length() < 1) {
				agentParameters = null;
			}
		return agentParameters;
		// ---------------------------------------------------------------------------------------
	}
	
	public synchronized void enableWeaverAgent( final String dynamicWeaverAgentJarPath) {
		File dynamicWeaverAgentFile = new File( dynamicWeaverAgentJarPath);
		boolean isAgentFound = false;
		Exception cause = null;
		try {
			JarFile jarFile = new JarFile( dynamicWeaverAgentFile.getAbsoluteFile());
			isAgentFound = hasManifestAttribute( jarFile, "Agent-Class");
		}
		catch( Exception exception) {
			cause = exception;
		}
		finally {
			if ( !isAgentFound) {
				IllegalArgumentException exception 
				= new IllegalArgumentException(
						String.format(
								"\"%1$s\" does not point to accessible valid .jar file containing " 
								+ "dynamically applicable Java Agent.",
								dynamicWeaverAgentJarPath
								)
						);
					if ( cause == null) {
						exception.initCause( cause);
					}
				throw exception;
			}
		}
		
		enableJavaAgent( 
				dynamicWeaverAgentFile.getAbsolutePath(), constructAgentParameters());
	}
	public synchronized void enableWeaverAgent( 
			final Class<?> dynamicWeaverAgentClass) throws URISyntaxException {
		File dynamicWeaverAgentFile 
		= new File( dynamicWeaverAgentClass.getProtectionDomain().getCodeSource().getLocation().toURI());
		
		enableJavaAgent( 
				dynamicWeaverAgentFile.getAbsolutePath(), constructAgentParameters());
	}
	
	/**
	 * Dynamically apply JavaAgent (what supposes to be AspectJ's Weaver agent) given by 
	 * {@link #setDynamicWeaverAgentJarFile(String)} method to current process. <br /> 
	 * If AspectJ's Weaver JavaAgent has already been enabled, then do nothing. <br />
	 * Importunate reminder: Load time weaving by AspectJ's Weaver Agent does not 
	 * happen most likely for the classes what have been already loaded before calling 
	 * this method, unless they will be reloaded.
	 */
//	public void enableWeaverAgent( 
//			String weaverJarPath, String additionalJarPaths) {
	public synchronized void enableWeaverAgent() {
		if ( isWeaverAgentEnabled()) { // aspectjweaver JavaAgent has already been enabled
			return;
		}
		
		String dynamicWeaverAgentJarPath = getDynamicWeaverAgentJarPath();
			if ( dynamicWeaverAgentJarPath == null) {
				throw new UnsupportedOperationException( 
						"Path to .jar file of dynamic weaver agent has not been set by " 
						+ "setDynamicWeaverAgentJarFile method.");
			}
			
		enableJavaAgent( 
				dynamicWeaverAgentJarPath, constructAgentParameters());
	}
	
	/**
	 * Delegates to {@link #enableWeaverAgent()} method to enable AspectJ's Weaver Agent dynamically. <br /> 
	 * 
	 * @param args First argument must be path to .jar file of dynamic weaver agent such as 
	 * DynamicAspectJWeaverAgent class. Second argument is for path to aspectjweaver.jar file. 
	 * Third argument is for path(s) of additional jar file(s).
	 */
	public static void main( String... args) {
		AspectJWeaverAgentLoader aspectJWeaverAgentLoader = new AspectJWeaverAgentLoader();
			for( int argsIndex = 0; argsIndex < args.length; argsIndex++) {
				switch( argsIndex) {
				case 0:
					aspectJWeaverAgentLoader.setDynamicWeaverAgentJarFile( args[ argsIndex]);
					break;
				case 1:
					aspectJWeaverAgentLoader.setAspectjWeaverJarFile( args[ argsIndex]);
					break;
				default:
					aspectJWeaverAgentLoader.addToAdditionalJarPaths( args[ argsIndex]);
					break;
				}
			}
		aspectJWeaverAgentLoader.enableWeaverAgent();
	}
}